home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / Libraries / CW GUSI 1.6.4 / src / GUSIFSp.cp < prev    next >
Text File  |  1995-10-25  |  23KB  |  1,148 lines

  1. /*********************************************************************
  2. Project    :    GUSI                -    Grand Unified Socket Interface
  3. File        :    GUSIFSp.cp        -    Dealing with paths
  4. Author    :    Matthias Neeracher <neeri@iis.ethz.ch>
  5. Language    :    MPW C++
  6.  
  7. $Log: GUSIFSp.cp,v $
  8. Revision 1.4  1994/12/30  20:00:56  neeri
  9. Add encoded FSSpecs.
  10.  
  11. Revision 1.3  1994/08/10  00:27:23  neeri
  12. Sanitized for universal headers.
  13. Fixed a deadly bug with multiple nonexistent path components.
  14.  
  15. Revision 1.2  1994/05/01  23:38:45  neeri
  16. Another rename() fix.
  17.  
  18. Revision 1.1  1994/03/08  22:06:44  neeri
  19. Initial revision
  20.  
  21. Revision 0.12  1994/02/04  00:00:00  neeri
  22. Make TFileSpec constructors preserve case to allow above
  23.  
  24. Revision 0.11  1993/10/24  00:00:00  neeri
  25. Allow changing case in a rename
  26.  
  27. Revision 0.10  1993/09/27  00:00:00  neeri
  28. FSpSmartMove
  29.  
  30. Revision 0.9  1993/07/17  00:00:00  neeri
  31. LastInfo
  32.  
  33. Revision 0.8  1993/06/21  00:00:00  neeri
  34. Throw out the inline
  35.  
  36. Revision 0.7  1993/03/01  00:00:00  neeri
  37. Bless
  38.  
  39. Revision 0.6  1993/02/06  00:00:00  neeri
  40. Use FSMakeFSSpec if possible
  41.  
  42. Revision 0.5  1993/01/15  00:00:00  neeri
  43. IsParentOf
  44.  
  45. Revision 0.4  1992/11/15  00:00:00  neeri
  46. Rename GUSIFSp_P.h to TFileSpec.h (there we go again)
  47.  
  48. Revision 0.3  1992/11/15  00:00:00  neeri
  49. Forgot a few consts
  50.  
  51. Revision 0.2  1992/09/12  00:00:00  neeri
  52. Renamed Paths.h to GUSIFSp_P.h
  53.  
  54. Revision 0.1  1992/09/06  00:00:00  neeri
  55. Clear ioACUser
  56.  
  57. *********************************************************************/
  58.  
  59. #include "GUSI_P.h"
  60. #include "TFileSpec.h"
  61.  
  62. #include <Errors.h>
  63. #include <Memory.h>
  64. #include <Aliases.h>
  65. #include <Resources.h>
  66. #include <string.h>
  67. #include <errno.h>
  68. #include <TextUtils.h>
  69. #include <PLStringFuncs.h>
  70.  
  71. #pragma segment GUSI
  72.  
  73. #define ROOT_MAGIC_COOKIE    666
  74.  
  75. OSErr         TFileSpec::error;
  76. short         TFileSpec::curVol;
  77. long            TFileSpec::curDir    =    -1;
  78. CInfoPBRec    TFileSpec::lastInfo;
  79.  
  80. OSErr TFileSpec::ChDir(const TFileSpec & spec)
  81. {
  82.     TFileSpec nudir(spec);
  83.     
  84.     nudir += (StringPtr) "\p";
  85.     
  86.     if (error)
  87.         return error;
  88.     
  89.     curVol    =    nudir.vRefNum;
  90.     curDir    =    nudir.parID;
  91.  
  92.     return noErr;
  93. }
  94.  
  95. static OSErr CurrentDir(short & vRefNum, long & parID)
  96. {
  97.     OSErr        error;
  98.     WDPBRec    vol;
  99.     Str63        name;
  100.     
  101.     vol.ioNamePtr    =    name;
  102.     
  103.     if (error = PBHGetVolSync(&vol))
  104.         return error;
  105.         
  106.     vRefNum    =    vol.ioWDVRefNum;
  107.     parID        =    vol.ioWDDirID;
  108.     
  109.     return noErr;
  110. }
  111.  
  112. OSErr TFileSpec::DefaultDir()
  113. {
  114.     if (curDir != -1)    {
  115.         vRefNum    =    curVol;
  116.         parID        =    curDir;
  117.         
  118.         return noErr;
  119.     } else
  120.         return error = CurrentDir(vRefNum, parID);
  121. }
  122.  
  123. OSErr TFileSpec::FindVol(short index)
  124. {
  125.     if (name[0] || index>0) {
  126.         ParamBlockRec    vol;
  127.         
  128.         vol.volumeParam.ioNamePtr    =    name;
  129.         vol.volumeParam.ioVolIndex    =    index;
  130.         
  131.         if (error = PBGetVInfoSync(&vol))
  132.             return error;
  133.         
  134.         vRefNum    =    vol.volumeParam.ioVRefNum;
  135.     } else {
  136.         error     =     noErr;
  137.         vRefNum    =     0;
  138.     }
  139.     
  140.     parID        =    fsRtParID;
  141.     
  142.     return error;
  143. }
  144.  
  145. void TFileSpec::Root()
  146. {
  147.     vRefNum = ROOT_MAGIC_COOKIE;
  148.     parID   = 0;
  149.     name[0] = 0;
  150.     
  151.     error = noErr;
  152. }
  153.  
  154. Boolean TFileSpec::IsRoot()
  155. {
  156.     return (!parID && vRefNum == ROOT_MAGIC_COOKIE);
  157. }
  158.  
  159. OSErr TFileSpec::Default()
  160. {
  161.     if (!DefaultDir())
  162.         --*this;
  163.     
  164.     return error;
  165. }
  166.  
  167. TFileSpec::TFileSpec(const TFileSpec & spec)
  168. {
  169.     vRefNum = spec.vRefNum;
  170.     parID   = spec.parID;
  171.     if (spec.name[0] < 64)
  172.         PLstrcpy(name, spec.name);
  173.     else
  174.         memcpy(name, spec.name, 64);
  175. }
  176.  
  177. TFileSpec::TFileSpec(const FSSpec & spec, Boolean useAlias)                            
  178. {
  179.     vRefNum = spec.vRefNum;
  180.     parID   = spec.parID;
  181.     if (spec.name[0] < 64)
  182.         PLstrcpy(name, spec.name);
  183.     else
  184.         memcpy(name, spec.name, 64);
  185.  
  186.     if (!useAlias && hasAlias)
  187.         Resolve();
  188. }
  189.  
  190. TFileSpec::TFileSpec(short vRefNum, long parID, ConstStr31Param name, Boolean useAlias)
  191. {
  192.     OSErr    err;
  193.     
  194.     if (!hasMakeFSSpec || 
  195.         ((err = FSMakeFSSpec(vRefNum, parID, name, this)) && (err != fnfErr))
  196.     ) {
  197.         this->vRefNum    =    vRefNum;
  198.         this->parID        =    parID;
  199.         memcpy(this->name, name, *name+1);
  200.     }
  201.     
  202.     if (!useAlias && hasAlias)
  203.         Resolve();    
  204.         
  205.     if (EqualString(this->name, name, false, true))
  206.         memcpy(this->name, name, *name+1);        
  207. }
  208.  
  209. TFileSpec::TFileSpec(short wd, ConstStr31Param name, Boolean useAlias)
  210. {
  211.     OSErr    err;
  212.     
  213.     if (!hasMakeFSSpec || 
  214.         ((err = FSMakeFSSpec(wd, 0, name, this)) && (err != fnfErr))
  215.     ) {
  216.         WDPBRec     wdPB;
  217.         
  218.         wdPB.ioNamePtr     = nil;
  219.         wdPB.ioVRefNum     = wd;
  220.         wdPB.ioWDIndex     = 0;
  221.         wdPB.ioWDProcID     = 0;
  222.         
  223.         /* Change the Working Directory number in vRefnum into a real vRefnum */
  224.         /* and DirID. The real vRefnum is returned in ioVRefnum, and the real */
  225.         /* DirID is returned in ioWDDirID. */
  226.         
  227.         if (error = PBGetWDInfoSync(&wdPB))
  228.             return;
  229.         
  230.         vRefNum    = wdPB.ioWDVRefNum;
  231.         parID        = wdPB.ioWDDirID;
  232.         memcpy(this->name, name, *name+1);
  233.     }
  234.     
  235.     if (!useAlias && hasAlias)
  236.         Resolve();
  237.         
  238.     if (EqualString(this->name, name, false, true))
  239.         memcpy(this->name, name, *name+1);        
  240. }
  241.  
  242. TFileSpec::TFileSpec(OSType object, short vol, long dir)
  243. {
  244.     if (object == kTempFileType && dir) {
  245.         vRefNum    =    vol;
  246.         parID        =    dir;
  247.     } else if (
  248.         error = 
  249.             FindFolder(
  250.                 vol, (object == kTempFileType) ? kTemporaryFolderType : object,
  251.                 true, &vRefNum, &parID)
  252.     ) 
  253.         return;
  254.         
  255.     if (object == kTempFileType) {
  256.         static long nr = -1;
  257.         
  258.         PLstrcpy(name, (StringPtr) "\ptmp00000");
  259.         
  260.         do {
  261.             nr = (nr + 1) % 100000;
  262.             
  263.             sprintf((char *) name+4, "%05ld", nr);
  264.         } while (Exists());
  265.     } else {
  266.         *this -= 1;
  267.     }
  268. }
  269.  
  270. TFileSpec::TFileSpec(short fRefNum)
  271. {
  272.     FCBPBRec            fcb;
  273.     Str255            fname;
  274.  
  275.     fcb.ioNamePtr    =     name;
  276.     fcb.ioRefNum    =    fRefNum;
  277.     fcb.ioFCBIndx    =     0;
  278.  
  279.     if (error = PBGetFCBInfoSync(&fcb))
  280.         return;
  281.  
  282.     vRefNum     =    fcb.ioFCBVRefNum;
  283.     parID        =    fcb.ioFCBParID;
  284. }
  285.  
  286. void TFileSpec::Bless()
  287. {
  288.     if (hasMakeFSSpec) {
  289.         FSSpec    spec    =    *this;    // Dont know what happens to an aliased name
  290.     
  291.         error = FSMakeFSSpec(spec.vRefNum, spec.parID, spec.name, this);
  292.         
  293.         if (EqualString(spec.name, name, false, true))
  294.             memcpy(name, spec.name, *spec.name+1);        
  295.     } else
  296.         error = noErr;
  297. }
  298.  
  299. #define    maxPathLen 512
  300.  
  301. static char fullPath[maxPathLen];
  302.  
  303. /* Prefix a path name component to path and update path */
  304. static void CopyComponent(char *& path, const unsigned char * component, Boolean colon)
  305. {
  306.     if (colon)
  307.         *--path = ':';
  308.     path -= *component;
  309.     memmove(path, component+1, *component);
  310. }
  311.  
  312. /* Convert an FSSpec into a full pathname. The pathname is accumulated on the
  313.    high end of path to avoid excessive copying.
  314. */
  315.  
  316.  
  317. char * TFileSpec::FullPath() const
  318. {
  319.     char *         curPath    =    fullPath + maxPathLen - 1;
  320.     StringPtr    scratch    =    (StringPtr) fullPath;
  321.     *curPath = 0;
  322.     
  323.     CopyComponent(curPath, name, parID == fsRtParID);
  324.     
  325.    lastInfo.dirInfo.ioNamePtr = scratch;
  326.  
  327.    for (
  328.        lastInfo.dirInfo.ioDrParID = parID;
  329.        lastInfo.dirInfo.ioDrParID != fsRtParID;
  330.    ) {
  331.       lastInfo.dirInfo.ioVRefNum     = vRefNum;
  332.       lastInfo.dirInfo.ioFDirIndex     = -1;
  333.       lastInfo.dirInfo.ioDrDirID     = lastInfo.dirInfo.ioDrParID;
  334.  
  335.       if (error = PBGetCatInfoSync(&lastInfo))
  336.              return "";
  337.         CopyComponent(curPath, scratch, true);
  338.   }
  339.  
  340.     return curPath;
  341. }
  342.  
  343. char * TFileSpec::FullAliasPath() const
  344. {
  345.     char *         curPath    =    fullPath + maxPathLen - 1;
  346.     StringPtr    scratch    =    (StringPtr) fullPath;
  347.     *curPath = 0;
  348.     
  349.     CInfoPBRec    info;
  350.     
  351.     if (error = CatInfo(info))
  352.         return nil;
  353.         
  354.     if (!hasAlias || !IsAlias(info)) {
  355.         error = resFNotFound;    // Close enough for Government work
  356.         
  357.         return nil;
  358.     }
  359.     short         oldRes    =    CurResFile();
  360.     short         res         =     FSpOpenResFile(this, fsRdPerm);
  361.     AliasHandle    alias;
  362.     if (res == -1) {
  363.         error =     ResError();
  364.         alias    =    nil;
  365.     } else {
  366.         if (alias = (AliasHandle) Get1Resource('alis', 0))
  367.             DetachResource((Handle) alias);
  368.         else
  369.             error = ResError();
  370.         
  371.         CloseResFile(res);
  372.     }
  373.     UseResFile(oldRes);
  374.     
  375.     if (!alias)
  376.         return nil;
  377.     
  378.     /*
  379.         Build path from target up to root.  Separate with colons.
  380.     */
  381.     for (short index = 0; !(error = GetAliasInfo(alias, index, scratch)); ++index) 
  382.         if (*fullPath) 
  383.             CopyComponent(curPath, scratch, index != 0);
  384.         else
  385.             break;
  386.     
  387.     if (!error && !(error = GetAliasInfo(alias, asiVolumeName, scratch)))
  388.         CopyComponent(curPath, scratch, true);
  389.  
  390.     DisposeHandle((Handle) alias);
  391.     
  392.     return error ? nil : curPath;
  393. }
  394. char * TFileSpec::RelPath() const
  395. {
  396.     short            curVRef;
  397.     long            curDirID;
  398.     
  399.     if (CurrentDir(curVRef, curDirID))
  400.         return FullPath();
  401.     else
  402.         return RelPath(curVRef, curDirID);
  403. }
  404.  
  405. char * TFileSpec::RelPath(const FSSpec & dir) const
  406. {
  407.     TFileSpec    directory(dir);
  408.     
  409.     if (directory.Error())
  410.         return FullPath();
  411.     
  412.     ++directory;
  413.  
  414.     if (directory.Error())
  415.         return FullPath();
  416.     else
  417.         return RelPath(directory.vRefNum, directory.parID);
  418. }
  419.         
  420. char * TFileSpec::RelPath(short curVRef, long curDirID) const
  421. {
  422.     char *        curPath;
  423.     
  424.     /* Special case: a volume was specified */
  425.     if (parID == fsRtParID)    {
  426.         if (vRefNum == curVRef && curDirID == fsRtDirID)
  427.             curPath = fullPath;
  428.         else {
  429.             memcpy(fullPath, name+1, *name);
  430.             curPath        =    fullPath+*name;
  431.         }
  432.         
  433.         curPath[0]    =    ':';
  434.         curPath[1]    =    0;
  435.         
  436.         return fullPath;
  437.     }
  438.  
  439.     lastInfo.dirInfo.ioNamePtr        =    (StringPtr)fullPath;
  440.     lastInfo.dirInfo.ioVRefNum     =     curVRef;
  441.     lastInfo.dirInfo.ioFDirIndex     =     -1;
  442.     lastInfo.dirInfo.ioDrDirID     =     curDirID;
  443.     
  444.     if (error = PBGetCatInfoSync(&lastInfo))
  445.         return "";
  446.     
  447.     if (curVRef == vRefNum 
  448.         && lastInfo.dirInfo.ioDrParID == parID
  449.         && !memcmp (fullPath, name, *fullPath+1)
  450.     )    {
  451.         fullPath[0]    =    ':';
  452.         fullPath[1] =     0;
  453.         
  454.         return fullPath;
  455.     }
  456.         
  457.     fullPath[maxPathLen-1]    =    0;
  458.     curPath                         =    fullPath+maxPathLen-*name-1;
  459.     
  460.     memcpy(curPath, name+1, *name);
  461.     
  462.    lastInfo.dirInfo.ioNamePtr = (StringPtr) fullPath;
  463.    lastInfo.dirInfo.ioDrParID = parID;
  464.  
  465.    do {
  466.       *--curPath    =    ':';
  467.          
  468.         /* Test fur current directory */
  469.         if (curVRef == vRefNum && curDirID == lastInfo.dirInfo.ioDrParID)
  470.             return strchr(curPath+1, ':') ? curPath : curPath+1;
  471.             
  472.         lastInfo.dirInfo.ioVRefNum     = vRefNum;
  473.         lastInfo.dirInfo.ioFDirIndex = -1;
  474.         lastInfo.dirInfo.ioDrDirID     = lastInfo.dirInfo.ioDrParID;
  475.         
  476.         if (error = PBGetCatInfoSync(&lastInfo))
  477.             return "";
  478.         curPath    -=    *fullPath;
  479.         memmove(curPath, fullPath+1, *fullPath);
  480.    } while (lastInfo.dirInfo.ioDrDirID != fsRtDirID);
  481.  
  482.     return curPath;
  483. }    
  484.  
  485. // Give encoded path. Encoding is:
  486. //
  487. // 1 byte:   DC1  (ASCII 0x11)
  488. // 4 bytes:  Volume reference number in zero-padded hex
  489. // 8 bytes:  Directory ID in zero-padded hex
  490. // n bytes:  Partial pathname, starting with ':'
  491. //
  492. // Needless to say, passing encoded values to anything but a GUSI routine is 
  493. // a bad idea.
  494. char * TFileSpec::Encode() const
  495. {
  496.     sprintf(fullPath, "\021%04hX%08X:", vRefNum, parID);
  497.     
  498.     memcpy(fullPath + 14, name+1, name[0]);
  499.     fullPath[14+name[0]] = 0;
  500.     
  501.     return fullPath;
  502. }
  503.  
  504. OSErr    TFileSpec::CatInfo(CInfoPBRec & info, Boolean dirInfo) const
  505. {
  506.    info.dirInfo.ioVRefNum         = vRefNum;
  507.    info.dirInfo.ioDrDirID         = parID;
  508.    info.dirInfo.ioNamePtr         = (StringPtr) name;
  509.    info.dirInfo.ioFDirIndex     = dirInfo ? -1 : 0;
  510.     info.dirInfo.ioACUser         = 0;
  511.         
  512.    return error = PBGetCatInfoSync(&info);
  513. }
  514.  
  515. TFileSpec TFileSpec::operator--()
  516. {
  517.     if (parID == fsRtParID)
  518.         Root();
  519.     else {
  520.           CatInfo(lastInfo, true);
  521.         
  522.         if (!error)
  523.             parID    = lastInfo.dirInfo.ioDrParID;
  524.     }
  525.     
  526.     return *this;
  527. }
  528.  
  529. TFileSpec TFileSpec::operator++()
  530. {
  531.     if (IsRoot()) {
  532.         vRefNum    =    0;
  533.         parID        =    fsRtParID;
  534.         name[0]    =    0;
  535.         
  536.         goto punt;
  537.     } 
  538.             
  539.    if (CatInfo(lastInfo))
  540.         goto punt;
  541.     
  542.     // Resolve if an alias
  543.     
  544.     if (IsAlias(lastInfo))
  545.         if (Resolve(lastInfo) || CatInfo(lastInfo))
  546.             goto punt;
  547.     
  548.     if (IsFile(lastInfo)) {
  549.         error = afpObjectTypeErr;
  550.         
  551.         goto punt;
  552.     }
  553.     
  554.     parID    = lastInfo.dirInfo.ioDrDirID;
  555.  
  556. punt:    
  557.     return *this;
  558. }
  559.  
  560. TFileSpec TFileSpec::operator-=(int levels)
  561. {
  562.     while (levels-- > 0)    {
  563.         --*this;
  564.         if (this->Error())
  565.             break;
  566.     }
  567.     
  568.     return *this;
  569. }
  570.  
  571. TFileSpec TFileSpec::operator-(int levels) const
  572. {
  573.     TFileSpec    spec    =    *this;
  574.     
  575.     return spec -= levels;
  576. }
  577.  
  578. OSErr TFileSpec::Resolve(const CInfoPBRec & info)
  579. {
  580.     Boolean        isFolder;
  581.     Boolean        wasAlias;
  582.     
  583.     return error = 
  584.         (hasAlias && IsAlias(info)) ? 
  585.             ResolveAliasFile(this, true, &isFolder, &wasAlias) :
  586.             noErr;
  587. }
  588.  
  589. OSErr TFileSpec::Resolve(Boolean gently)
  590. {
  591.     CatInfo(lastInfo);
  592.     
  593.     if (error)
  594.         if (gently)
  595.             return error = noErr;
  596.         else
  597.             return error;
  598.     else
  599.         return Resolve(lastInfo);
  600. }
  601.  
  602. Boolean TFileSpec::Exists() const
  603. {
  604.     Boolean        res;
  605.     
  606.     res     = !CatInfo(lastInfo);
  607.     error    =    noErr;
  608.     
  609.     return res;
  610. }
  611.  
  612. Boolean TFileSpec::operator==(const TFileSpec & other) const
  613. {
  614.     return     vRefNum == other.vRefNum 
  615.         &&        parID == other.parID 
  616.         &&     EqualString(name, other.name, false, true);
  617. }
  618.  
  619. Boolean TFileSpec::operator!=(const TFileSpec & other) const
  620. {
  621.     return     vRefNum != other.vRefNum 
  622.         ||        parID != other.parID 
  623.         ||     !EqualString(name, other.name, false, true);
  624. }
  625.  
  626. Boolean TFileSpec::IsParentOf(const TFileSpec & other) const
  627. {
  628.     for (TFileSpec oth = other - 1; !oth.Error() && *this != oth; --oth)
  629.         if (oth == *this)
  630.             return true;
  631.     
  632.     return false;
  633. }
  634.  
  635. TFileSpec TFileSpec::AddPathComponent(const char * name, int length)
  636. {
  637.     if (length > 63) {
  638.         error = bdNamErr;
  639.         
  640.         goto punt;
  641.     }
  642.  
  643.     ++(*this);
  644.     
  645.     if (error)
  646.         goto punt;
  647.         
  648.     memcpy(this->name+1, name, *this->name = length);
  649.         
  650.     if (parID == fsRtParID)
  651.         FindVol(-1);
  652.  
  653. punt:    
  654.     return *this;
  655. }
  656.  
  657. TFileSpec TFileSpec::operator+=(ConstStr31Param name)
  658. {
  659.     return AddPathComponent((const char *) name+1, *name);
  660. }
  661.  
  662. TFileSpec TFileSpec::operator+(ConstStr31Param name) const
  663. {
  664.     TFileSpec    spec    =    *this;
  665.     
  666.     return spec += name;
  667. }
  668.  
  669. TFileSpec TFileSpec::operator+=(const char * name)
  670. {
  671.     return AddPathComponent(name, strlen(name));
  672. }
  673.  
  674. TFileSpec TFileSpec::operator+(const char * name) const
  675. {
  676.     TFileSpec    spec    =    *this;
  677.     
  678.     return spec += name;
  679. }
  680.  
  681. TFileSpec TFileSpec::operator[](short index) const
  682. {
  683.     TFileSpec    spec     = *this;
  684.     
  685.     if (spec.parID == fsRtParID) 
  686.         spec.FindVol(index);
  687.     else {
  688.        lastInfo.dirInfo.ioVRefNum     = spec.vRefNum;
  689.        lastInfo.dirInfo.ioDrDirID         = spec.parID;
  690.        lastInfo.dirInfo.ioNamePtr     = spec.name;
  691.        lastInfo.dirInfo.ioFDirIndex     = index;
  692.     
  693.        error = PBGetCatInfoSync(&lastInfo);
  694.     }
  695.     
  696.     return spec;
  697. }
  698.  
  699. static Boolean ReadNHex(const char * nr, int count, unsigned long * val)
  700. {
  701.     for (*val = 0; count--; ++nr) {
  702.         *val <<= 4;
  703.         switch (*nr) {
  704.         case '0':
  705.         case '1':
  706.         case '2':
  707.         case '3':
  708.         case '4':
  709.         case '5':
  710.         case '6':
  711.         case '7':
  712.         case '8':
  713.         case '9':
  714.             *val |= *nr - '0';    
  715.             break;
  716.         case 'a':
  717.         case 'b':
  718.         case 'c':
  719.         case 'd':
  720.         case 'e':
  721.         case 'f':
  722.             *val |= *nr - 'a' + 10;    
  723.             break;
  724.         case 'A':
  725.         case 'B':
  726.         case 'C':
  727.         case 'D':
  728.         case 'E':
  729.         case 'F':
  730.             *val |= *nr - 'A' + 10;    
  731.             break;
  732.         default:
  733.             return false;
  734.         }
  735.     }
  736.     
  737.     return true;
  738. }
  739.  
  740. Boolean TFileSpec::IsEncodedFSSpec(const char * path, Boolean useAlias)
  741. {
  742.     // To be as robust as possible against conflicts, we won't tolerate any
  743.     // deviations from the pattern:
  744.     
  745.     // 1 byte:   DC1  (ASCII 0x11)
  746.     // 4 bytes:  Volume reference number in zero-padded hex
  747.     // 8 bytes:  Directory ID in zero-padded hex
  748.     // n bytes:  Partial pathname, starting with ':'
  749.  
  750.     if (*path++ != 0x11)        // Magic character DC1
  751.         return false;
  752.     
  753.     unsigned long     val;
  754.     
  755.     if (ReadNHex(path, 4, &val)) {
  756.         vRefNum = short(val);
  757.         if (ReadNHex(path+4, 8, &val)) {
  758.             parID = long(val);
  759.             path += 12;
  760.             
  761.             if (*path == ':' && !strchr(path+1, ':'))
  762.                 if (!path[1]) {
  763.                     --(*this);
  764.                     
  765.                     return true;
  766.                 } else {
  767.                     *(char *)path     = strlen(path+1);
  768.                     
  769.                     if (hasMakeFSSpec) {
  770.                         if (error = FSMakeFSSpec(vRefNum, parID, (ConstStr255Param) path, this))
  771.                             if (error == fnfErr)
  772.                                 error = noErr;
  773.                     } else {
  774.                         this->vRefNum    =    vRefNum;
  775.                         this->parID        =    parID;
  776.                         memcpy(this->name, path, *path+1);
  777.                     }
  778.                     
  779.                     if (!useAlias && hasAlias)
  780.                         Resolve();    
  781.                         
  782.                     if (EqualString(this->name, (ConstStr255Param) path, false, true))
  783.                         memcpy(this->name, path, *path+1);
  784.                     
  785.                     *(char *)path     = ':';
  786.                     
  787.                     return true;
  788.                 }
  789.         }
  790.     }
  791.     return false;
  792. }
  793.  
  794. TFileSpec::TFileSpec(const char * path, Boolean useAlias)
  795. {
  796.     if (IsEncodedFSSpec(path, useAlias))
  797.         return;
  798.  
  799.     int            pathLen    =     int(strlen(path));
  800.     StringPtr    name        =    (StringPtr) fullPath;
  801.     char *        nextPath;
  802.         
  803.     if (hasMakeFSSpec) {
  804.         DefaultDir();
  805.         
  806.         CopyC2PStr(path, name);
  807.         
  808.         switch (error = FSMakeFSSpec(vRefNum, parID, name, this)) {
  809.         case fnfErr:
  810.             error = noErr;
  811.             
  812.             return;
  813.         case noErr:
  814.             if (!useAlias && hasAlias)
  815.                 Resolve();
  816.  
  817.             if (nextPath = (char *) strrchr(path, ':'))
  818.                 path = nextPath+1;
  819.  
  820.             CopyC2PStr(path, name);
  821.         
  822.             if (EqualString(this->name, name, false, true))
  823.                 memcpy(this->name, name, *name+1);        
  824.             
  825.             return;
  826.         default:
  827.             break;
  828.         }
  829.     }
  830.     
  831.     if (path[0] == ':' || !(nextPath = (char *) strchr(path, ':'))) {
  832.         Default();
  833.         
  834.         if (*path == ':')
  835.             ++path;
  836.     } else {
  837.         if (nextPath - (char *) path > 62) {
  838.             error = bdNamErr;
  839.             
  840.             return;
  841.         }
  842.  
  843.         memcpy(name+1, (char *) path, *name = nextPath - (char *)  path + 1);
  844.             
  845.         if (FindVol(-1))
  846.             return;
  847.         
  848.         path = nextPath + 1;
  849.     }
  850.         
  851.     if (error)
  852.         return;
  853.     
  854.     while (*path) {
  855.         if (*path == ':')    {
  856.             --*this;
  857.             ++path;
  858.             
  859.             if (error)
  860.                 return;
  861.             else
  862.                 continue;
  863.         }
  864.         
  865.         if (nextPath = (char *) strchr(path, ':'))
  866.             *nextPath = 0;
  867.         
  868.         *this += path;
  869.  
  870.         if (nextPath)
  871.             *nextPath = ':';
  872.  
  873.         if (error)
  874.             return;
  875.             
  876.         if (nextPath)
  877.             path = nextPath + 1;
  878.         else
  879.             break;
  880.     }
  881.  
  882.     if (!useAlias && hasAlias)
  883.         Resolve();
  884. }
  885.  
  886. /* Convert a FSSpec into a full pathname. */
  887. char * FSp2FullPath(const FSSpec * desc)
  888. {
  889.     TFileSpec    spec(*desc);
  890.     
  891.     return spec.FullPath();
  892. }
  893.  
  894. /* Convert a FSSpec into a relative pathname. */
  895. char * FSp2RelPath(const FSSpec * desc)
  896. {
  897.     TFileSpec    spec(*desc);
  898.     
  899.     return spec.RelPath();
  900. }
  901.  
  902. /* Works like FSp2RelPath, but regarding to a specified directory.
  903. */
  904. char * FSp2DirRelPath(const FSSpec * desc, const FSSpec * dir)
  905. {
  906.     TFileSpec    spec(*desc);
  907.     
  908.     return spec.RelPath(*dir);
  909. }
  910.  
  911. /* Encode a FSSpec*/
  912. char * FSp2Encoding(const FSSpec * desc)
  913. {
  914.     TFileSpec    spec(*desc);
  915.     
  916.     return spec.Encode();
  917. }
  918.  
  919. /* Convert a working directory & file name into a FSSpec. */
  920. OSErr WD2FSSpec(short wd, ConstStr31Param name, FSSpec * desc)
  921. {
  922.     TFileSpec    spec(wd, name);
  923.     
  924.     *desc = spec;
  925.     
  926.     return spec.Error();
  927. }
  928.  
  929. /* Convert a pathname into a file spec. */
  930. OSErr Path2FSSpec(const char * path, FSSpec * desc)
  931. {
  932.     TFileSpec    spec(path);
  933.     
  934.     *desc = spec;
  935.     
  936.     return spec.Error();
  937. }
  938.  
  939. /* Convert a working directory & file name into a FSSpec. */
  940. OSErr Special2FSSpec(OSType object, short vol, long dirID, FSSpec * desc)
  941. {
  942.     TFileSpec    spec(object, vol, dirID);
  943.     
  944.     *desc = spec;
  945.     
  946.     return spec.Error();
  947. }
  948.  
  949. /* Return FSSpec of (vRefNum, parID) */
  950. OSErr FSpUp(FSSpec * desc)
  951. {
  952.     TFileSpec    spec(*desc);
  953.  
  954.     *desc = --spec;
  955.     
  956.     return spec.Error();
  957. }
  958.  
  959. /* Return FSSpec of file in directory denoted by desc */
  960. OSErr FSpDown(FSSpec * desc, ConstStr31Param name)
  961. {
  962.     TFileSpec    spec(*desc);
  963.  
  964.     *desc    = spec + name;
  965.     
  966.     return spec.Error();
  967. }
  968.  
  969. /* Call GetCatInfo for file system object. */
  970. OSErr    FSpCatInfo(const FSSpec * desc, CInfoPBRec * info)
  971. {
  972.     OSErr            err;
  973.     TFileSpec    spec(*desc);
  974.     
  975.     if (err = spec.CatInfo(*info))
  976.         return err;
  977.     
  978.     info->hFileInfo.ioNamePtr = (StringPtr) desc->name;
  979.     
  980.     return noErr;
  981. }
  982.  
  983. /* Return FSSpec of nth file in directory denoted by (vRefNum, parID) */
  984. OSErr FSpIndex(FSSpec * desc, short n)
  985. {
  986.     TFileSpec    spec(*desc);
  987.     
  988.     *desc = spec[n];
  989.     
  990.     return spec.Error();
  991. }
  992.  
  993. static OSErr DefaultVRef(short & vRef)
  994. {
  995.     OSErr                err;
  996.     ParamBlockRec    vol;
  997.     
  998.     vol.volumeParam.ioNamePtr = nil;
  999.     
  1000.     if (err = PBGetVolSync(&vol))
  1001.         return err;
  1002.     
  1003.     vRef = vol.volumeParam.ioVRefNum;
  1004.     
  1005.     return noErr;
  1006. }
  1007.  
  1008. OSErr FSpSmartMove(const FSSpec * from, const FSSpec * to)
  1009. {
  1010.     OSErr            err;
  1011.     TFileSpec    fromspec(*from, true);
  1012.     TFileSpec    tospec(*to, true);
  1013.     TFileSpec    toparent = tospec - 1;
  1014.     TFileSpec    corner;
  1015.     CInfoPBRec    fromInfo;
  1016.     CInfoPBRec    toInfo;
  1017.     
  1018.     if (!fromspec.vRefNum)
  1019.         if (err = DefaultVRef(fromspec.vRefNum))
  1020.             return err;
  1021.             
  1022.     if (!tospec.vRefNum)
  1023.         if (err = DefaultVRef(tospec.vRefNum))
  1024.             return err;
  1025.             
  1026.     if (fromspec.vRefNum != tospec.vRefNum)
  1027.         return diffVolErr;
  1028.         
  1029.     Boolean        diffname    =    !EqualString(fromspec.name, tospec.name, false, true);
  1030.     Boolean        diffdir    =    fromspec.parID != tospec.parID;
  1031.     Boolean        toexists = !tospec.CatInfo(toInfo);
  1032.     Boolean        lockfrom;
  1033.     Boolean        lockto;
  1034.     TFileSpec    tmpto;
  1035.     
  1036.     if (err = fromspec.CatInfo(fromInfo))
  1037.         return err;
  1038.     
  1039.     if (!IsFile(fromInfo) && fromspec.IsParentOf(tospec))
  1040.         return badMovErr;
  1041.                 
  1042.     if (lockfrom = IsFile(fromInfo) && fromInfo.hFileInfo.ioFlAttrib & 0x01)
  1043.         HRstFLock(fromspec.vRefNum, fromspec.parID, fromspec.name);
  1044.  
  1045.     if (!diffname && !diffdir)    { /* Files are identical, except possibly for case */
  1046.         err = noErr;
  1047.         
  1048.         goto cleanupcase;
  1049.     }
  1050.     
  1051.     if (toexists)
  1052.         if (!IsFile(toInfo) && toInfo.dirInfo.ioDrNmFls)
  1053.             return fBsyErr;
  1054.         else {
  1055.             tmpto = TFileSpec(kTempFileType, tospec.vRefNum, tospec.parID);
  1056.             
  1057.             if (lockto = IsFile(toInfo) && toInfo.hFileInfo.ioFlAttrib & 0x01)
  1058.                 HRstFLock(tospec.vRefNum, tospec.parID, tospec.name);
  1059.                 
  1060.             if (err = HRename(tospec.vRefNum, tospec.parID, tospec.name, tmpto.name))
  1061.                 return err;
  1062.         }
  1063.         
  1064.     if (!diffdir) {
  1065.         err = HRename(fromspec.vRefNum, fromspec.parID, fromspec.name, tospec.name);
  1066.         
  1067.         goto cleanuptmp;
  1068.     } else if (!diffname) {
  1069.         err = 
  1070.             CatMove(
  1071.                 fromspec.vRefNum, fromspec.parID, fromspec.name, 
  1072.                 toparent.parID, toparent.name);
  1073.         
  1074.         goto cleanuptmp;
  1075.     }
  1076.     
  1077.     corner = TFileSpec(fromspec.vRefNum, fromspec.parID, tospec.name);
  1078.     
  1079.     if (!corner.Exists()) {
  1080.         err = HRename(fromspec.vRefNum, fromspec.parID, fromspec.name, tospec.name);
  1081.         
  1082.         if (!err)
  1083.             if (err = 
  1084.                 CatMove(
  1085.                     fromspec.vRefNum, fromspec.parID, tospec.name,
  1086.                     toparent.parID, toparent.name)
  1087.             )
  1088.                 HRename(fromspec.vRefNum, fromspec.parID, tospec.name, fromspec.name);
  1089.         
  1090.         goto cleanuptmp;
  1091.     }
  1092.         
  1093.     {
  1094.         TFileSpec    secondcorner(kTempFileType, tospec.vRefNum, tospec.parID);
  1095.         
  1096.         memcpy(corner.name, secondcorner.name, secondcorner.name[0]+1);
  1097.         
  1098.         while (corner.Exists() || secondcorner.Exists()) {
  1099.             TFileSpec newcorner(kTempFileType, secondcorner.vRefNum, secondcorner.parID);
  1100.             
  1101.             memcpy(corner.name, newcorner.name, newcorner.name[0]+1);
  1102.             memcpy(secondcorner.name, newcorner.name, newcorner.name[0]+1);
  1103.         }
  1104.     }
  1105.     
  1106.     err = HRename(fromspec.vRefNum, fromspec.parID, fromspec.name, corner.name);
  1107.     
  1108.     if (!err)
  1109.         if (err = 
  1110.             CatMove(
  1111.                 fromspec.vRefNum, fromspec.parID, corner.name,
  1112.                 toparent.parID, toparent.name)
  1113.         ) 
  1114.             HRename(fromspec.vRefNum, fromspec.parID, corner.name, fromspec.name);
  1115.         else if (err =
  1116.             HRename(tospec.vRefNum, tospec.parID, corner.name, tospec.name)
  1117.         ) {
  1118.             TFileSpec fromparent = fromspec - 1;
  1119.             
  1120.             CatMove(
  1121.                 tospec.vRefNum, tospec.parID, corner.name,
  1122.                 fromparent.parID, fromparent.name);
  1123.             HRename(fromspec.vRefNum, fromspec.parID, corner.name, fromspec.name);            
  1124.         }
  1125.     
  1126. cleanuptmp:
  1127.     if (toexists)
  1128.         if (err) {
  1129.             HRename(tmpto.vRefNum, tmpto.parID, tmpto.name, tospec.name);
  1130.             
  1131.             if (lockto)
  1132.                 HSetFLock(tospec.vRefNum, tospec.parID, tospec.name);
  1133.         } else
  1134.             HDelete(tmpto.vRefNum, tmpto.parID, tmpto.name);
  1135.  
  1136. cleanupcase:
  1137.     if (!err && !diffname)
  1138.         err = HRename(tospec.vRefNum, tospec.parID, tospec.name, tospec.name);
  1139.     if (lockfrom)
  1140.         if (err)
  1141.             HSetFLock(fromspec.vRefNum, fromspec.parID, fromspec.name);
  1142.         else
  1143.             HSetFLock(tospec.vRefNum, tospec.parID, tospec.name);
  1144.         
  1145.     return err;
  1146. }
  1147.  
  1148.